﻿<ComClass(ClsVector2d.ClassId, ClsVector2d.InterfaceId, ClsVector2d.EventsId)>
Public Class ClsVector2d

#Region "COM GUID"
    ' 这些 GUID 提供此类的 COM 标识 
    ' 及其 COM 接口。若更改它们，则现有的
    ' 客户端将不再能访问此类。
    Public Const ClassId As String = "ac90236c-25ab-4022-8d08-0c95c7d31ca0"
    Public Const InterfaceId As String = "8fff6c5f-4d3f-47e7-a04d-1dafd96ad3bf"
    Public Const EventsId As String = "1fa26ca4-c82a-43ed-a38d-b3036e497605"
    Private Const Pi As Double = Math.PI
#End Region

#Region "字段和属性"
    Private xValue As Double
    Private yValue As Double


    ''' <summary>
    ''' 横坐标
    ''' </summary>
    ''' <returns></returns>
    Public Property X As Double
        Get
            Return xValue
        End Get
        Set(value As Double)
            xValue = value
        End Set
    End Property

    ''' <summary>
    ''' 纵坐标
    ''' </summary>
    ''' <returns></returns>
    Public Property Y As Double
        Get
            Return yValue
        End Get
        Set(value As Double)
            yValue = value
        End Set
    End Property

    ''' <summary>
    ''' 平面向量的模长
    ''' </summary>
    ''' <returns></returns>
    Public ReadOnly Property Length As Double
        Get
            Return Math.Sqrt(Me.X * Me.X + Me.Y * Me.Y)
        End Get
    End Property


    Public ReadOnly Property PerpendicularVector As ClsVector2d
        Get
            Return New ClsVector2d() With {.X = (Me.Y * -1), .Y = Me.X}
        End Get
    End Property

    Public ReadOnly Property AngelToXAxis As Double
        Get
            Select Case Me.Location
                Case 平面坐标系位置.X正半轴
                    Return 0
                Case 平面坐标系位置.X负半轴
                    Return Math.PI
                Case 平面坐标系位置.Y正半轴
                    Return Math.PI * 0.5
                Case 平面坐标系位置.Y负半轴
                    Return Math.PI * 1.5
                Case 平面坐标系位置.第一象限
                    Return Math.Asin(Me.Y / Me.Length)
                Case 平面坐标系位置.第二象限, 平面坐标系位置.第三象限
                    Return Math.PI - Math.Asin(Me.Y / Me.Length)
                Case 平面坐标系位置.第四象限
                    Return Math.PI * 2 + Math.Asin(Me.Y / Me.Length)
                Case Else
                    Return 0
            End Select
        End Get
    End Property

    ''' <summary>
    ''' 平面向量的单位向量
    ''' </summary>
    ''' <returns></returns>
    Public ReadOnly Property UnitVector As ClsVector2d
        Get
            Return Me.DivideBy(Me.Length)
        End Get
    End Property
    Public ReadOnly Property NegateVector As ClsVector2d
        Get
            Return Me.Multiply(-1)
        End Get
    End Property

    Public ReadOnly Property Normal As ClsVector2d
        Get
            Return Me.PerpendicularVector.DivideBy(Me.Length)
        End Get
    End Property
    'x axis
    Public ReadOnly Property XAxis As ClsVector2d
        Get
            Dim vectord As New ClsVector2d With {.X = 1, .Y = 0}
            Return vectord.UnitVector
        End Get
    End Property
    Public ReadOnly Property IsUnitVector2d As Boolean
        Get
            Return Math.Abs(Me.Length - 1) <= 0.00000001
        End Get
    End Property
    Public ReadOnly Property YAxis As ClsVector2d
        Get
            Dim vectord As New ClsVector2d With {.X = 0, .Y = 1}
            Return vectord.UnitVector
        End Get
    End Property

    Public ReadOnly Property Location As 平面坐标系位置
        Get
            Dim m As New ClsMath

            If m.IsZero(Me.X) And m.IsZero(Me.Y) Then
                Return 平面坐标系位置.原点
            Else
                If m.IsZero(Me.X) Then
                    If Me.Y > 0 Then
                        Return 平面坐标系位置.Y正半轴
                    Else
                        Return 平面坐标系位置.Y负半轴
                    End If
                ElseIf m.IsZero(Me.Y) Then
                    If Me.X > 0 Then
                        Return 平面坐标系位置.X正半轴
                    Else
                        Return 平面坐标系位置.X负半轴
                    End If
                Else
                    If Me.X > 0 Then
                        If Me.Y > 0 Then
                            Return 平面坐标系位置.第一象限
                        Else
                            Return 平面坐标系位置.第四象限
                        End If
                    Else
                        If Me.Y > 0 Then
                            Return 平面坐标系位置.第二象限
                        Else
                            Return 平面坐标系位置.第三象限
                        End If
                    End If
                End If
            End If
        End Get

    End Property

    ''' <summary>
    ''' 转字符串格式
    ''' </summary>
    ''' <returns></returns>
    Public ReadOnly Property StringStyle As String
        Get
            Return $"({Me.X},{Me.Y})"
        End Get
    End Property

#End Region

#Region "构造函数,初始化"

    ' 可创建的 COM 类必须具有一个不带参数的 Public Sub New() 
    ' 否则， 将不会在 
    ' COM 注册表中注册此类，且无法通过
    ' CreateObject 创建此类。
    Public Sub New()
        MyBase.New()
    End Sub
    ''' <summary>
    ''' 构造函数
    ''' </summary>
    ''' <param name="x1">传入的横坐标</param>
    ''' <param name="y1">传入的纵坐标</param>
    Public Sub CreatorByXy(x1 As Double, y1 As Double)
        Me.X = x1
        Me.Y = y1
    End Sub
    ''' <summary>
    ''' 根据角度创建单位向量
    ''' </summary>
    ''' <param name="ang"></param>
    Public Sub CreatorUnitVictor2dByAngle(ang As Double)
        Me.X = Math.Cos(ang)
        Me.Y = Math.Sin(ang)
    End Sub

    ''' <summary>
    ''' 构造函数
    ''' </summary>
    ''' <param name="arr">2个元素的double</param>
    Public Sub CreatorByArray(arr() As Double)
        Me.X = arr(LBound(arr))
        Me.Y = arr(LBound(arr) + 1)
    End Sub
    ''' <summary>
    ''' (x,y) 格式的字符串转点对象
    ''' </summary>
    ''' <param name="listPt">(x,y) 格式的字符串</param>
    ''' <param name="split">x与y之间的分割符.默认未逗号</param>
    Public Sub CreatorByString(listPt As String, Optional split As String = ",")
        Dim ptwithspliter = listPt.Trim().Substring(1, listPt.Trim().Length - 2)
        Dim arr = ptwithspliter.Split({split}, StringSplitOptions.RemoveEmptyEntries)
        Me.X = arr(LBound(arr))
        Me.Y = arr(LBound(arr) + 1)
    End Sub

#End Region

#Region "四则运算"
    ''' <summary>
    ''' 实现向量与标量乘法
    ''' </summary>
    ''' <param name="number"></param>
    ''' <returns></returns>
    Public Function Multiply(number As Double) As ClsVector2d
        Return New ClsVector2d() With {.X = Me.X * number, .Y = Me.Y * number}
    End Function

    ''' <summary>
    ''' '实现向量与标量除法
    ''' </summary>
    ''' <param name="number"></param>
    ''' <returns></returns>
    Public Function DivideBy(number As Double) As ClsVector2d
        Return New ClsVector2d() With {.X = Me.X / number, .Y = Me.Y / number}
    End Function

    Public Function Add(ByVal a As ClsVector2d) As ClsVector2d
        Return New ClsVector2d() With {
        .X = (Me.X + a.X),
        .Y = (Me.Y + a.Y)
    }
    End Function

    Public Function Subtraction(ByVal a As ClsVector2d) As ClsVector2d
        Return New ClsVector2d() With {
        .X = (Me.X - a.X),
        .Y = (Me.Y - a.Y)
    }
    End Function
    ''' <summary>
    ''' 二维叉乘
    ''' </summary>
    ''' <param name="v1">给定向量</param>
    ''' <returns>正数,当前向量在给定向量的逆时针方向;负数当前向量在给定向量的顺时针的方向</returns>
    Public Function CrossProductWith(ByVal v1 As ClsVector2d) As Double
        Return ((Me.X * v1.Y) - (Me.Y * v1.X))
    End Function

    ''' <summary>
    ''' 点乘,
    ''' </summary>
    ''' <param name="v1"></param>
    ''' <returns></returns>
    Public Function DotProductWith(ByVal v1 As ClsVector2d) As Double
        Return ((Me.X * v1.X) + (Me.Y * v1.Y))
    End Function
#End Region


    ''' <summary>
    ''' 判定当前的平面向量与给定的平面向量V1是否互相平行
    ''' </summary>
    ''' <param name="v1">给定的平面向量</param>
    ''' <returns>布尔类型</returns>
    Public Function IsParallelTo(ByVal v1 As ClsVector2d) As Boolean
        Return (Math.Abs(Me.CrossProductWith(v1)) <= 0.00000001)
    End Function

    Public Function GetAngleTo(ByVal v1 As ClsVector2d) As Double
        Dim cos As Double = (Me.DotProductWith(v1) / (Me.Length * v1.Length))
        If (cos >= 1) Then
            Return 0
        End If
        If (cos <= -1) Then
            Return Math.PI
        End If
        Return Math.Acos(cos)
    End Function

    ''' <summary>
    ''' 判定当前的平面向量与给定的平面向量V1是否互相垂直
    ''' </summary>
    ''' <param name="v1">给定的平面向量</param>
    ''' <returns>布尔类型</returns>
    Public Function IsPerpendicularTo(ByVal v1 As ClsVector2d) As Boolean
        Return (Math.Abs(Me.DotProductWith(v1)) <= 0.00000001)
    End Function

    Public Function ProjectionOnVector(ByVal v1 As ClsVector2d) As ClsVector2d
        Return v1.UnitVector.Multiply(Me.DotProductWith(v1.UnitVector))
    End Function

    Public Function ToPoint2D() As ClsPoint2d
        Return New ClsPoint2d With {.X = Me.X, .Y = Me.Y}
    End Function

    Public Function IsEqualTo(ByVal checkedVector As ClsVector2d) As Boolean
        Return ((Math.Abs(CDbl((Me.X - checkedVector.X))) <= 0.00000001) AndAlso (Math.Abs(CDbl((Me.Y - checkedVector.Y))) <= 0.00000001))
    End Function

#Region "com不可见内容"
    Public Overrides Function ToString() As String
        Return $"长度={Me.Length },角度={Me.AngelToXAxis * Math.PI / 180}"
    End Function
    ''' <summary>
    ''' 不要调入需要对com可见的内容中
    ''' </summary>
    ''' <param name="ang"></param>
    Public Sub New(ang As Double)
        Call CreatorUnitVictor2dByAngle(ang)
    End Sub
    ''' <summary>
    ''' 不要调入需要对com可见的内容中
    ''' </summary>
    ''' <param name="x1"></param>
    ''' <param name="y1"></param>
    Public Sub New(x1 As Double, y1 As Double)
        Call CreatorByXy(x1, y1)
    End Sub
    ''' <summary>
    ''' 不要调入需要对com可见的内容中
    ''' </summary>
    ''' <param name="arr"></param>
    Public Sub New(arr() As Double)
        Call CreatorByArray(arr)
    End Sub
#End Region

#Region "向量的变换"

    ''' <summary>
    ''' 旋转生成新的点
    ''' </summary>
    ''' <param name="angle">旋转角度,弧度制</param>
    Public Function Rotate(angle As Double) As ClsVector2d
        Return New ClsVector2d With
            {
                 .X = Me.X * Math.Cos(angle) - Me.Y * Math.Sin(angle),
                 .Y = Me.X * Math.Sin(angle) + Me.Y * Math.Cos(angle)
            }

    End Function
#End Region
End Class


